/*
    Copyright (C) 2012-2013 de4dot@gmail.com

    Permission is hereby granted, free of charge, to any person obtaining
    a copy of this software and associated documentation files (the
    "Software"), to deal in the Software without restriction, including
    without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to
    permit persons to whom the Software is furnished to do so, subject to
    the following conditions:

    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace dnlib.DotNet {
	/// <summary>
	/// Compares types
	/// </summary>
	public sealed class TypeEqualityComparer : IEqualityComparer<IType>, IEqualityComparer<ITypeDefOrRef>, IEqualityComparer<TypeRef>, IEqualityComparer<TypeDef>, IEqualityComparer<TypeSpec>, IEqualityComparer<TypeSig>, IEqualityComparer<ExportedType> {
		readonly SigComparerOptions options;

		/// <summary>
		/// Default instance
		/// </summary>
		public static readonly TypeEqualityComparer Instance = new TypeEqualityComparer(0);

		/// <summary>
		/// Case insensitive names
		/// </summary>
		public static readonly TypeEqualityComparer CaseInsensitive = new TypeEqualityComparer(SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="options">Comparison options</param>
		public TypeEqualityComparer(SigComparerOptions options) {
			this.options = options;
		}

		/// <inheritdoc/>
		public bool Equals(IType x, IType y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(IType obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(ITypeDefOrRef x, ITypeDefOrRef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(ITypeDefOrRef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(TypeRef x, TypeRef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(TypeRef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(TypeDef x, TypeDef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(TypeDef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(TypeSpec x, TypeSpec y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(TypeSpec obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(TypeSig x, TypeSig y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(TypeSig obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(ExportedType x, ExportedType y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(ExportedType obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}
	}

	/// <summary>
	/// Compares fields
	/// </summary>
	public sealed class FieldEqualityComparer : IEqualityComparer<IField>, IEqualityComparer<FieldDef>, IEqualityComparer<MemberRef> {
		readonly SigComparerOptions options;

		/// <summary>
		/// Compares the declaring types
		/// </summary>
		public static readonly FieldEqualityComparer CompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType);

		/// <summary>
		/// Doesn't compare the declaring types
		/// </summary>
		public static readonly FieldEqualityComparer DontCompareDeclaringTypes = new FieldEqualityComparer(0);

		/// <summary>
		/// Compares the declaring types, case insensitive names
		/// </summary>
		public static readonly FieldEqualityComparer CaseInsensitiveCompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType | SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Doesn't compare the declaring types, case insensitive names
		/// </summary>
		public static readonly FieldEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="options">Comparison options</param>
		public FieldEqualityComparer(SigComparerOptions options) {
			this.options = options;
		}

		/// <inheritdoc/>
		public bool Equals(IField x, IField y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(IField obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(FieldDef x, FieldDef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(FieldDef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(MemberRef x, MemberRef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(MemberRef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}
	}

	/// <summary>
	/// Compares methods
	/// </summary>
	public sealed class MethodEqualityComparer : IEqualityComparer<IMethod>, IEqualityComparer<IMethodDefOrRef>, IEqualityComparer<MethodDef>, IEqualityComparer<MemberRef>, IEqualityComparer<MethodSpec> {
		readonly SigComparerOptions options;

		/// <summary>
		/// Compares the declaring types
		/// </summary>
		public static readonly MethodEqualityComparer CompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType);

		/// <summary>
		/// Doesn't compare the declaring types
		/// </summary>
		public static readonly MethodEqualityComparer DontCompareDeclaringTypes = new MethodEqualityComparer(0);

		/// <summary>
		/// Compares the declaring types, case insensitive names
		/// </summary>
		public static readonly MethodEqualityComparer CaseInsensitiveCompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType | SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Doesn't compare the declaring types, case insensitive names
		/// </summary>
		public static readonly MethodEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="options">Comparison options</param>
		public MethodEqualityComparer(SigComparerOptions options) {
			this.options = options;
		}

		/// <inheritdoc/>
		public bool Equals(IMethod x, IMethod y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(IMethod obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(IMethodDefOrRef x, IMethodDefOrRef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(IMethodDefOrRef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(MethodDef x, MethodDef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(MethodDef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(MemberRef x, MemberRef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(MemberRef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(MethodSpec x, MethodSpec y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(MethodSpec obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}
	}

	/// <summary>
	/// Compares properties
	/// </summary>
	public sealed class PropertyEqualityComparer : IEqualityComparer<PropertyDef> {
		readonly SigComparerOptions options;

		/// <summary>
		/// Compares the declaring types
		/// </summary>
		public static readonly PropertyEqualityComparer CompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.ComparePropertyDeclaringType);

		/// <summary>
		/// Doesn't compare the declaring types
		/// </summary>
		public static readonly PropertyEqualityComparer DontCompareDeclaringTypes = new PropertyEqualityComparer(0);

		/// <summary>
		/// Compares the declaring types, case insensitive names
		/// </summary>
		public static readonly PropertyEqualityComparer CaseInsensitiveCompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.ComparePropertyDeclaringType | SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Doesn't compare the declaring types, case insensitive names
		/// </summary>
		public static readonly PropertyEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="options">Comparison options</param>
		public PropertyEqualityComparer(SigComparerOptions options) {
			this.options = options;
		}

		/// <inheritdoc/>
		public bool Equals(PropertyDef x, PropertyDef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(PropertyDef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}
	}

	/// <summary>
	/// Compares events
	/// </summary>
	public sealed class EventEqualityComparer : IEqualityComparer<EventDef> {
		readonly SigComparerOptions options;

		/// <summary>
		/// Compares the declaring types
		/// </summary>
		public static readonly EventEqualityComparer CompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CompareEventDeclaringType);

		/// <summary>
		/// Doesn't compare the declaring types
		/// </summary>
		public static readonly EventEqualityComparer DontCompareDeclaringTypes = new EventEqualityComparer(0);

		/// <summary>
		/// Compares the declaring types, case insensitive names
		/// </summary>
		public static readonly EventEqualityComparer CaseInsensitiveCompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CompareEventDeclaringType | SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Doesn't compare the declaring types, case insensitive names
		/// </summary>
		public static readonly EventEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="options">Comparison options</param>
		public EventEqualityComparer(SigComparerOptions options) {
			this.options = options;
		}

		/// <inheritdoc/>
		public bool Equals(EventDef x, EventDef y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(EventDef obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}
	}

	/// <summary>
	/// Compares calling convention signatures
	/// </summary>
	public sealed class SignatureEqualityComparer : IEqualityComparer<CallingConventionSig>, IEqualityComparer<MethodBaseSig>, IEqualityComparer<MethodSig>, IEqualityComparer<PropertySig>, IEqualityComparer<FieldSig>, IEqualityComparer<LocalSig>, IEqualityComparer<GenericInstMethodSig> {
		readonly SigComparerOptions options;

		/// <summary>
		/// Default instance
		/// </summary>
		public static readonly SignatureEqualityComparer Instance = new SignatureEqualityComparer(0);

		/// <summary>
		/// Case insensitive names
		/// </summary>
		public static readonly SignatureEqualityComparer CaseInsensitive = new SignatureEqualityComparer(SigComparerOptions.CaseInsensitiveAll);

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="options">Comparison options</param>
		public SignatureEqualityComparer(SigComparerOptions options) {
			this.options = options;
		}

		/// <inheritdoc/>
		public bool Equals(CallingConventionSig x, CallingConventionSig y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(CallingConventionSig obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(MethodBaseSig x, MethodBaseSig y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(MethodBaseSig obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(MethodSig x, MethodSig y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(MethodSig obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(PropertySig x, PropertySig y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(PropertySig obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(FieldSig x, FieldSig y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(FieldSig obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(LocalSig x, LocalSig y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(LocalSig obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}

		/// <inheritdoc/>
		public bool Equals(GenericInstMethodSig x, GenericInstMethodSig y) {
			return new SigComparer { Options = options }.Equals(x, y);
		}

		/// <inheritdoc/>
		public int GetHashCode(GenericInstMethodSig obj) {
			return new SigComparer { Options = options }.GetHashCode(obj);
		}
	}

	/// <summary>
	/// Decides how to compare types, sigs, etc
	/// </summary>
	[Flags]
	public enum SigComparerOptions : uint {
		/// <summary>
		/// Don't compare a type's (assembly/module) scope
		/// </summary>
		DontCompareTypeScope = 1,

		/// <summary>
		/// Compares a method/field's declaring type.
		/// </summary>
		CompareMethodFieldDeclaringType = 2,

		/// <summary>
		/// Compares a property's declaring type
		/// </summary>
		ComparePropertyDeclaringType = 4,

		/// <summary>
		/// Compares an event's declaring type
		/// </summary>
		CompareEventDeclaringType = 8,

		/// <summary>
		/// Compares parameters after a sentinel in method sigs. Should not be enabled when
		/// comparing <see cref="MethodSig"/>s against <see cref="MethodInfo"/>s since it's
		/// not possible to get those sentinel params from a <see cref="MethodInfo"/>.
		/// </summary>
		CompareSentinelParams = 0x10,

		/// <summary>
		/// Compares assembly public key token
		/// </summary>
		CompareAssemblyPublicKeyToken = 0x20,

		/// <summary>
		/// Compares assembly version
		/// </summary>
		CompareAssemblyVersion = 0x40,

		/// <summary>
		/// Compares assembly locale
		/// </summary>
		CompareAssemblyLocale = 0x80,

		/// <summary>
		/// If set, a <see cref="TypeRef"/> and an <see cref="ExportedType"/> can reference the
		/// global <c>&lt;Module&gt;</c> type.
		/// </summary>
		TypeRefCanReferenceGlobalType = 0x100,

		/// <summary>
		/// Don't compare a method/property's return type
		/// </summary>
		DontCompareReturnType = 0x200,

		/// <summary>
		/// If set, all generic parameters are replaced with their generic arguments prior
		/// to comparing types. You should enable this when comparing a method, field, property
		/// or an event to a <see cref="MethodInfo"/>, <see cref="FieldInfo"/>,
		/// <see cref="PropertyInfo"/> or an <see cref="EventInfo"/> if the owner type could
		/// be a generic instance type.
		/// </summary>
		SubstituteGenericParameters = 0x400,

		/// <summary>
		/// Type namespaces are case insensitive
		/// </summary>
		CaseInsensitiveTypeNamespaces = 0x800,

		/// <summary>
		/// Type names (not namespaces) are case insensitive
		/// </summary>
		CaseInsensitiveTypeNames = 0x1000,

		/// <summary>
		/// Type names and namespaces are case insensitive
		/// </summary>
		CaseInsensitiveTypes = CaseInsensitiveTypeNamespaces | CaseInsensitiveTypeNames,

		/// <summary>
		/// Method and field names are case insensitive
		/// </summary>
		CaseInsensitiveMethodFieldNames = 0x2000,

		/// <summary>
		/// Property names are case insensitive
		/// </summary>
		CaseInsensitivePropertyNames = 0x4000,

		/// <summary>
		/// Event names are case insensitive
		/// </summary>
		CaseInsensitiveEventNames = 0x8000,

		/// <summary>
		/// Type namespaces, type names, method names, field names, property names
		/// and event names are all case insensitive
		/// </summary>
		CaseInsensitiveAll = CaseInsensitiveTypeNamespaces | CaseInsensitiveTypeNames |
						CaseInsensitiveMethodFieldNames | CaseInsensitivePropertyNames |
						CaseInsensitiveEventNames,

		/// <summary>
		/// A field that is <see cref="FieldAttributes.PrivateScope"/> can compare equal to
		/// a <see cref="MemberRef"/>
		/// </summary>
		PrivateScopeFieldIsComparable = 0x10000,

		/// <summary>
		/// A method that is <see cref="MethodAttributes.PrivateScope"/> can compare equal to
		/// a <see cref="MemberRef"/>
		/// </summary>
		PrivateScopeMethodIsComparable = 0x20000,

		/// <summary>
		/// Raw (bit by bit) comparison of signatures. This matches what the CLR does when it
		/// compares signatures. This means that metadata tokens will be compared.
		/// </summary>
		RawSignatureCompare = 0x40000,

		/// <summary>
		/// Ignore required and optional modifiers when comparing <see cref="TypeSig"/>s.
		/// They're already ignored when comparing eg. a <see cref="TypeSig"/> with a
		/// <see cref="TypeRef"/>.
		/// </summary>
		IgnoreModifiers = 0x80000,

		/// <summary>
		/// By default, all module and assembly compares when they're both the system library
		/// (eg. mscorlib or System.Runtime.dll) return true, even if they're really different,
		/// eg. mscorlib (.NET 2.0) vs mscorlib (Windows CE). If this flag is set, the system
		/// library is compared just like any other module/assembly.
		/// </summary>
		MscorlibIsNotSpecial = 0x100000,
	}

	/// <summary>
	/// Compares types, signatures, methods, fields, properties, events
	/// </summary>
	public struct SigComparer {
		const int HASHCODE_MAGIC_GLOBAL_TYPE = 1654396648;
		const int HASHCODE_MAGIC_NESTED_TYPE = -1049070942;
		const int HASHCODE_MAGIC_ET_MODULE = -299744851;
		const int HASHCODE_MAGIC_ET_VALUEARRAY = -674970533;
		const int HASHCODE_MAGIC_ET_GENERICINST = -2050514639;
		const int HASHCODE_MAGIC_ET_VAR = 1288450097;
		const int HASHCODE_MAGIC_ET_MVAR = -990598495;
		const int HASHCODE_MAGIC_ET_ARRAY = -96331531;
		const int HASHCODE_MAGIC_ET_SZARRAY = 871833535;
		const int HASHCODE_MAGIC_ET_BYREF = -634749586;
		const int HASHCODE_MAGIC_ET_PTR = 1976400808;
		const int HASHCODE_MAGIC_ET_SENTINEL = 68439620;

		RecursionCounter recursionCounter;
		SigComparerOptions options;
		GenericArguments genericArguments;

		/// <summary>
		/// Gets/sets the options
		/// </summary>
		public SigComparerOptions Options {
			get { return options; }
			set { options = value; }
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.DontCompareTypeScope"/> bit
		/// </summary>
		public bool DontCompareTypeScope {
			get { return (options & SigComparerOptions.DontCompareTypeScope) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.DontCompareTypeScope;
				else
					options &= ~SigComparerOptions.DontCompareTypeScope;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CompareMethodFieldDeclaringType"/> bit
		/// </summary>
		public bool CompareMethodFieldDeclaringType {
			get { return (options & SigComparerOptions.CompareMethodFieldDeclaringType) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CompareMethodFieldDeclaringType;
				else
					options &= ~SigComparerOptions.CompareMethodFieldDeclaringType;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.ComparePropertyDeclaringType"/> bit
		/// </summary>
		public bool ComparePropertyDeclaringType {
			get { return (options & SigComparerOptions.ComparePropertyDeclaringType) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.ComparePropertyDeclaringType;
				else
					options &= ~SigComparerOptions.ComparePropertyDeclaringType;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CompareEventDeclaringType"/> bit
		/// </summary>
		public bool CompareEventDeclaringType {
			get { return (options & SigComparerOptions.CompareEventDeclaringType) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CompareEventDeclaringType;
				else
					options &= ~SigComparerOptions.CompareEventDeclaringType;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CompareSentinelParams"/> bit
		/// </summary>
		public bool CompareSentinelParams {
			get { return (options & SigComparerOptions.CompareSentinelParams) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CompareSentinelParams;
				else
					options &= ~SigComparerOptions.CompareSentinelParams;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CompareAssemblyPublicKeyToken"/> bit
		/// </summary>
		public bool CompareAssemblyPublicKeyToken {
			get { return (options & SigComparerOptions.CompareAssemblyPublicKeyToken) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CompareAssemblyPublicKeyToken;
				else
					options &= ~SigComparerOptions.CompareAssemblyPublicKeyToken;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CompareAssemblyVersion"/> bit
		/// </summary>
		public bool CompareAssemblyVersion {
			get { return (options & SigComparerOptions.CompareAssemblyVersion) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CompareAssemblyVersion;
				else
					options &= ~SigComparerOptions.CompareAssemblyVersion;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CompareAssemblyLocale"/> bit
		/// </summary>
		public bool CompareAssemblyLocale {
			get { return (options & SigComparerOptions.CompareAssemblyLocale) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CompareAssemblyLocale;
				else
					options &= ~SigComparerOptions.CompareAssemblyLocale;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.TypeRefCanReferenceGlobalType"/> bit
		/// </summary>
		public bool TypeRefCanReferenceGlobalType {
			get { return (options & SigComparerOptions.TypeRefCanReferenceGlobalType) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.TypeRefCanReferenceGlobalType;
				else
					options &= ~SigComparerOptions.TypeRefCanReferenceGlobalType;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.DontCompareReturnType"/> bit
		/// </summary>
		public bool DontCompareReturnType {
			get { return (options & SigComparerOptions.DontCompareReturnType) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.DontCompareReturnType;
				else
					options &= ~SigComparerOptions.DontCompareReturnType;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.SubstituteGenericParameters"/> bit
		/// </summary>
		public bool SubstituteGenericParameters {
			get { return (options & SigComparerOptions.SubstituteGenericParameters) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.SubstituteGenericParameters;
				else
					options &= ~SigComparerOptions.SubstituteGenericParameters;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CaseInsensitiveTypeNamespaces"/> bit
		/// </summary>
		public bool CaseInsensitiveTypeNamespaces {
			get { return (options & SigComparerOptions.CaseInsensitiveTypeNamespaces) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CaseInsensitiveTypeNamespaces;
				else
					options &= ~SigComparerOptions.CaseInsensitiveTypeNamespaces;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CaseInsensitiveTypeNames"/> bit
		/// </summary>
		public bool CaseInsensitiveTypeNames {
			get { return (options & SigComparerOptions.CaseInsensitiveTypeNames) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CaseInsensitiveTypeNames;
				else
					options &= ~SigComparerOptions.CaseInsensitiveTypeNames;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CaseInsensitiveMethodFieldNames"/> bit
		/// </summary>
		public bool CaseInsensitiveMethodFieldNames {
			get { return (options & SigComparerOptions.CaseInsensitiveMethodFieldNames) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CaseInsensitiveMethodFieldNames;
				else
					options &= ~SigComparerOptions.CaseInsensitiveMethodFieldNames;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CaseInsensitivePropertyNames"/> bit
		/// </summary>
		public bool CaseInsensitivePropertyNames {
			get { return (options & SigComparerOptions.CaseInsensitivePropertyNames) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CaseInsensitivePropertyNames;
				else
					options &= ~SigComparerOptions.CaseInsensitivePropertyNames;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.CaseInsensitiveEventNames"/> bit
		/// </summary>
		public bool CaseInsensitiveEventNames {
			get { return (options & SigComparerOptions.CaseInsensitiveEventNames) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.CaseInsensitiveEventNames;
				else
					options &= ~SigComparerOptions.CaseInsensitiveEventNames;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.PrivateScopeFieldIsComparable"/> bit
		/// </summary>
		public bool PrivateScopeFieldIsComparable {
			get { return (options & SigComparerOptions.PrivateScopeFieldIsComparable) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.PrivateScopeFieldIsComparable;
				else
					options &= ~SigComparerOptions.PrivateScopeFieldIsComparable;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.PrivateScopeMethodIsComparable"/> bit
		/// </summary>
		public bool PrivateScopeMethodIsComparable {
			get { return (options & SigComparerOptions.PrivateScopeMethodIsComparable) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.PrivateScopeMethodIsComparable;
				else
					options &= ~SigComparerOptions.PrivateScopeMethodIsComparable;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.RawSignatureCompare"/> bit
		/// </summary>
		public bool RawSignatureCompare {
			get { return (options & SigComparerOptions.RawSignatureCompare) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.RawSignatureCompare;
				else
					options &= ~SigComparerOptions.RawSignatureCompare;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.IgnoreModifiers"/> bit
		/// </summary>
		public bool IgnoreModifiers {
			get { return (options & SigComparerOptions.IgnoreModifiers) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.IgnoreModifiers;
				else
					options &= ~SigComparerOptions.IgnoreModifiers;
			}
		}

		/// <summary>
		/// Gets/sets the <see cref="SigComparerOptions.MscorlibIsNotSpecial"/> bit
		/// </summary>
		public bool MscorlibIsNotSpecial {
			get { return (options & SigComparerOptions.MscorlibIsNotSpecial) != 0; }
			set {
				if (value)
					options |= SigComparerOptions.MscorlibIsNotSpecial;
				else
					options &= ~SigComparerOptions.MscorlibIsNotSpecial;
			}
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="options">Comparison options</param>
		public SigComparer(SigComparerOptions options) {
			this.recursionCounter = new RecursionCounter();
			this.options = options;
			this.genericArguments = null;
		}

		/// <summary>
		/// <see cref="ElementType.FnPtr"/> is mapped to <see cref="System.IntPtr"/>, so use
		/// the same hash code for both
		/// </summary>
		int GetHashCode_FnPtr_SystemIntPtr() {
			// ********************************************
			// IMPORTANT: This must match GetHashCode(TYPE)
			// ********************************************

			return GetHashCode_TypeNamespace("System") +
					GetHashCode_TypeName("IntPtr");
		}

		bool Equals_Names(bool caseInsensitive, UTF8String a, UTF8String b) {
			if (caseInsensitive)
				return UTF8String.ToSystemStringOrEmpty(a).Equals(UTF8String.ToSystemStringOrEmpty(b), StringComparison.OrdinalIgnoreCase);
			return UTF8String.Equals(a, b);
		}

		bool Equals_Names(bool caseInsensitive, string a, string b) {
			if (caseInsensitive)
				return (a ?? string.Empty).Equals(b ?? string.Empty, StringComparison.OrdinalIgnoreCase);
			return (a ?? string.Empty) == (b ?? string.Empty);
		}

		int GetHashCode_Name(bool caseInsensitive, string a) {
			if (caseInsensitive)
				return (a ?? string.Empty).ToUpperInvariant().GetHashCode();
			return (a ?? string.Empty).GetHashCode();
		}

		bool Equals_TypeNamespaces(UTF8String a, UTF8String b) {
			return Equals_Names(CaseInsensitiveTypeNamespaces, a, b);
		}

		bool Equals_TypeNamespaces(UTF8String a, string b) {
			return Equals_Names(CaseInsensitiveTypeNamespaces, UTF8String.ToSystemStringOrEmpty(a), b);
		}

		int GetHashCode_TypeNamespace(UTF8String a) {
			return GetHashCode_Name(CaseInsensitiveTypeNamespaces, UTF8String.ToSystemStringOrEmpty(a));
		}

		int GetHashCode_TypeNamespace(string a) {
			return GetHashCode_Name(CaseInsensitiveTypeNamespaces, a);
		}

		bool Equals_TypeNames(UTF8String a, UTF8String b) {
			return Equals_Names(CaseInsensitiveTypeNames, a, b);
		}

		bool Equals_TypeNames(UTF8String a, string b) {
			return Equals_Names(CaseInsensitiveTypeNames, UTF8String.ToSystemStringOrEmpty(a), b);
		}

		int GetHashCode_TypeName(UTF8String a) {
			return GetHashCode_Name(CaseInsensitiveTypeNames, UTF8String.ToSystemStringOrEmpty(a));
		}

		int GetHashCode_TypeName(string a) {
			return GetHashCode_Name(CaseInsensitiveTypeNames, a);
		}

		bool Equals_MethodFieldNames(UTF8String a, UTF8String b) {
			return Equals_Names(CaseInsensitiveMethodFieldNames, a, b);
		}

		bool Equals_MethodFieldNames(UTF8String a, string b) {
			return Equals_Names(CaseInsensitiveMethodFieldNames, UTF8String.ToSystemStringOrEmpty(a), b);
		}

		int GetHashCode_MethodFieldName(UTF8String a) {
			return GetHashCode_Name(CaseInsensitiveMethodFieldNames, UTF8String.ToSystemStringOrEmpty(a));
		}

		int GetHashCode_MethodFieldName(string a) {
			return GetHashCode_Name(CaseInsensitiveMethodFieldNames, a);
		}

		bool Equals_PropertyNames(UTF8String a, UTF8String b) {
			return Equals_Names(CaseInsensitivePropertyNames, a, b);
		}

		bool Equals_PropertyNames(UTF8String a, string b) {
			return Equals_Names(CaseInsensitivePropertyNames, UTF8String.ToSystemStringOrEmpty(a), b);
		}

		int GetHashCode_PropertyName(UTF8String a) {
			return GetHashCode_Name(CaseInsensitivePropertyNames, UTF8String.ToSystemStringOrEmpty(a));
		}

		int GetHashCode_PropertyName(string a) {
			return GetHashCode_Name(CaseInsensitivePropertyNames, a);
		}

		bool Equals_EventNames(UTF8String a, UTF8String b) {
			return Equals_Names(CaseInsensitiveEventNames, a, b);
		}

		bool Equals_EventNames(UTF8String a, string b) {
			return Equals_Names(CaseInsensitiveEventNames, UTF8String.ToSystemStringOrEmpty(a), b);
		}

		int GetHashCode_EventName(UTF8String a) {
			return GetHashCode_Name(CaseInsensitiveEventNames, UTF8String.ToSystemStringOrEmpty(a));
		}

		int GetHashCode_EventName(string a) {
			return GetHashCode_Name(CaseInsensitiveEventNames, a);
		}

		SigComparerOptions ClearOptions(SigComparerOptions flags) {
			var old = options;
			options &= ~flags;
			return old;
		}

		SigComparerOptions SetOptions(SigComparerOptions flags) {
			var old = options;
			options |= flags;
			return old;
		}

		void RestoreOptions(SigComparerOptions oldFlags) {
			options = oldFlags;
		}

		void InitializeGenericArguments() {
			if (genericArguments == null)
				genericArguments = new GenericArguments();
		}

		static GenericInstSig GetGenericInstanceType(IMemberRefParent parent) {
			var ts = parent as TypeSpec;
			if (ts == null)
				return null;
			return ts.TypeSig.RemoveModifiers() as GenericInstSig;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(IType a, IType b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			TypeDef tda, tdb;
			TypeRef tra, trb;
			TypeSpec tsa, tsb;
			TypeSig sa, sb;
			ExportedType eta, etb;

			if ((tda = a as TypeDef) != null & (tdb = b as TypeDef) != null)
				result = Equals(tda, tdb);
			else if ((tra = a as TypeRef) != null & (trb = b as TypeRef) != null)
				result = Equals(tra, trb);
			else if ((tsa = a as TypeSpec) != null & (tsb = b as TypeSpec) != null)
				result = Equals(tsa, tsb);
			else if ((sa = a as TypeSig) != null & (sb = b as TypeSig) != null)
				result = Equals(sa, sb);
			else if ((eta = a as ExportedType) != null & (etb = b as ExportedType) != null)
				result = Equals(eta, etb);
			else if (tda != null && trb != null)
				result = Equals(tda, trb);		// TypeDef vs TypeRef
			else if (tra != null && tdb != null)
				result = Equals(tdb, tra);		// TypeDef vs TypeRef
			else if (tda != null && tsb != null)
				result = Equals(tda, tsb);		// TypeDef vs TypeSpec
			else if (tsa != null && tdb != null)
				result = Equals(tdb, tsa);		// TypeDef vs TypeSpec
			else if (tda != null && sb != null)
				result = Equals(tda, sb);		// TypeDef vs TypeSig
			else if (sa != null && tdb != null)
				result = Equals(tdb, sa);		// TypeDef vs TypeSig
			else if (tda != null && etb != null)
				result = Equals(tda, etb);		// TypeDef vs ExportedType
			else if (eta != null && tdb != null)
				result = Equals(tdb, eta);		// TypeDef vs ExportedType
			else if (tra != null && tsb != null)
				result = Equals(tra, tsb);		// TypeRef vs TypeSpec
			else if (tsa != null && trb != null)
				result = Equals(trb, tsa);		// TypeRef vs TypeSpec
			else if (tra != null && sb != null)
				result = Equals(tra, sb);		// TypeRef vs TypeSig
			else if (sa != null && trb != null)
				result = Equals(trb, sa);		// TypeRef vs TypeSig
			else if (tra != null && etb != null)
				result = Equals(tra, etb);		// TypeRef vs ExportedType
			else if (eta != null && trb != null)
				result = Equals(trb, eta);		// TypeRef vs ExportedType
			else if (tsa != null && sb != null)
				result = Equals(tsa, sb);		// TypeSpec vs TypeSig
			else if (sa != null && tsb != null)
				result = Equals(tsb, sa);		// TypeSpec vs TypeSig
			else if (tsa != null && etb != null)
				result = Equals(tsa, etb);		// TypeSpec vs ExportedType
			else if (eta != null && tsb != null)
				result = Equals(tsb, eta);		// TypeSpec vs ExportedType
			else if (sa != null && etb != null)
				result = Equals(sa, etb);		// TypeSig vs ExportedType
			else if (eta != null && sb != null)
				result = Equals(sb, eta);		// TypeSig vs ExportedType
			else
				result = false;	// Should never be reached

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a type
		/// </summary>
		/// <param name="a">The type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(IType a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash;
			TypeDef td;
			TypeRef tr;
			TypeSpec ts;
			TypeSig sig;
			ExportedType et;

			if ((td = a as TypeDef) != null)
				hash = GetHashCode(td);
			else if ((tr = a as TypeRef) != null)
				hash = GetHashCode(tr);
			else if ((ts = a as TypeSpec) != null)
				hash = GetHashCode(ts);
			else if ((sig = a as TypeSig) != null)
				hash = GetHashCode(sig);
			else if ((et = a as ExportedType) != null)
				hash = GetHashCode(et);
			else
				hash = 0;	// Should never be reached

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeRef a, TypeDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeDef a, TypeRef b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			IModule bMod;
			AssemblyRef bAsm;
			TypeRef dtb;
			var scope = b.ResolutionScope;

			if (!Equals_TypeNames(a.Name, b.Name) || !Equals_TypeNamespaces(a.Namespace, b.Namespace))
				result = false;
			else if ((dtb = scope as TypeRef) != null)	// nested type
				result = Equals(a.DeclaringType, dtb);	// Compare enclosing types
			else if (a.DeclaringType != null) {
				// a is nested, b isn't
				result = false;
			}
			else if (DontCompareTypeScope)
				result = true;
			else if ((bMod = scope as IModule) != null) {	// 'b' is defined in the same assembly as 'a'
				result = Equals((IModule)a.Module, (IModule)bMod) &&
						Equals(a.DefinitionAssembly, b.DefinitionAssembly);
			}
			else if ((bAsm = scope as AssemblyRef) != null) {
				var aMod = a.Module;
				result = aMod != null && Equals(aMod.Assembly, bAsm);
			}
			else {
				result = false;
				//TODO: Handle the case where scope == null
			}

			if (result && !TypeRefCanReferenceGlobalType && a.IsGlobalModuleType)
				result = false;
			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(ExportedType a, TypeDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeDef a, ExportedType b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			ExportedType dtb;
			FileDef bFile;
			AssemblyRef bAsm;
			var scope = b.Implementation;

			if (!Equals_TypeNames(a.Name, b.TypeName) || !Equals_TypeNamespaces(a.Namespace, b.TypeNamespace))
				result = false;
			else if ((dtb = scope as ExportedType) != null) {	// nested type
				result = Equals(a.DeclaringType, dtb);	// Compare enclosing types
			}
			else if (a.DeclaringType != null) {
				result = false;	// a is nested, b isn't
			}
			else if (DontCompareTypeScope)
				result = true;
			else if ((bFile = scope as FileDef) != null) {
				result = Equals(a.Module, bFile) &&
						Equals(a.DefinitionAssembly, b.DefinitionAssembly);
			}
			else if ((bAsm = scope as AssemblyRef) != null) {
				var aMod = a.Module;
				result = aMod != null && Equals(aMod.Assembly, bAsm);
			}
			else
				result = false;

			if (result && !TypeRefCanReferenceGlobalType && a.IsGlobalModuleType)
				result = false;
			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSpec a, TypeDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeDef a, TypeSpec b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			return Equals(a, b.TypeSig);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSig a, TypeDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeDef a, TypeSig b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			//*************************************************************
			// If this code gets updated, update GetHashCode(TypeSig),
			// Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too
			//*************************************************************
			var b2 = b as TypeDefOrRefSig;
			if (b2 != null)
				result = Equals(a, (IType)b2.TypeDefOrRef);
			else if (b is ModifierSig || b is PinnedSig)
				result = Equals(a, b.Next);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSpec a, TypeRef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeRef a, TypeSpec b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			return Equals(a, b.TypeSig);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(ExportedType a, TypeRef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeRef a, ExportedType b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_TypeNames(a.Name, b.TypeName) &&
					Equals_TypeNamespaces(a.Namespace, b.TypeNamespace) &&
					EqualsScope(a, b);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSig a, TypeRef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeRef a, TypeSig b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			//*************************************************************
			// If this code gets updated, update GetHashCode(TypeSig),
			// Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too
			//*************************************************************
			var b2 = b as TypeDefOrRefSig;
			if (b2 != null)
				result = Equals(a, (IType)b2.TypeDefOrRef);
			else if (b is ModifierSig || b is PinnedSig)
				result = Equals(a, b.Next);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSig a, TypeSpec b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSpec a, TypeSig b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			return Equals(a.TypeSig, b);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(ExportedType a, TypeSpec b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSpec a, ExportedType b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			return Equals(a.TypeSig, b);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(ExportedType a, TypeSig b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSig a, ExportedType b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			//*************************************************************
			// If this code gets updated, update GetHashCode(TypeSig),
			// Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too
			//*************************************************************
			var a2 = a as TypeDefOrRefSig;
			if (a2 != null)
				result = Equals(a2.TypeDefOrRef, b);
			else if (a is ModifierSig || a is PinnedSig)
				result = Equals(a.Next, b);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		int GetHashCodeGlobalType() {
			// We don't always know the name+namespace of the global type, eg. when it's
			// referenced by a ModuleRef. Use the same hash for all global types.
			return HASHCODE_MAGIC_GLOBAL_TYPE;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeRef a, TypeRef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_TypeNames(a.Name, b.Name) &&
					Equals_TypeNamespaces(a.Namespace, b.Namespace) &&
					EqualsResolutionScope(a, b);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a type
		/// </summary>
		/// <param name="a">The type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(TypeRef a) {
			// ************************************************************************************
			// IMPORTANT: This hash code must match the Type/TypeRef/TypeDef/ExportedType
			// hash code and GetHashCode_FnPtr_SystemIntPtr() method
			// ************************************************************************************

			// See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType()
			if (a == null)
				return TypeRefCanReferenceGlobalType ? GetHashCodeGlobalType() : 0;
			int hash;
			hash = GetHashCode_TypeName(a.Name);
			if (a.ResolutionScope is TypeRef)
				hash += HASHCODE_MAGIC_NESTED_TYPE;
			else
				hash += GetHashCode_TypeNamespace(a.Namespace);
			return hash;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(ExportedType a, ExportedType b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_TypeNames(a.TypeName, b.TypeName) &&
					Equals_TypeNamespaces(a.TypeNamespace, b.TypeNamespace) &&
					EqualsImplementation(a, b);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a type
		/// </summary>
		/// <param name="a">The type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(ExportedType a) {
			// ************************************************************************************
			// IMPORTANT: This hash code must match the Type/TypeRef/TypeDef/ExportedType
			// hash code and GetHashCode_FnPtr_SystemIntPtr() method
			// ************************************************************************************

			// See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType()
			if (a == null)
				return TypeRefCanReferenceGlobalType ? GetHashCodeGlobalType() : 0;
			int hash;
			hash = GetHashCode_TypeName(a.TypeName);
			if (a.Implementation is ExportedType)
				hash += HASHCODE_MAGIC_NESTED_TYPE;
			else
				hash += GetHashCode_TypeNamespace(a.TypeNamespace);
			return hash;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeDef a, TypeDef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_TypeNames(a.Name, b.Name) &&
					Equals_TypeNamespaces(a.Namespace, b.Namespace) &&
					Equals(a.DeclaringType, b.DeclaringType) &&
					(DontCompareTypeScope || Equals(a.Module, b.Module));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a type
		/// </summary>
		/// <param name="a">The type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(TypeDef a) {
			// ************************************************************************************
			// IMPORTANT: This hash code must match the Type/TypeRef/TypeDef/ExportedType
			// hash code and GetHashCode_FnPtr_SystemIntPtr() method
			// ************************************************************************************

			// See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType()
			if (a == null || a.IsGlobalModuleType)
				return GetHashCodeGlobalType();
			int hash;
			hash = GetHashCode_TypeName(a.Name);
			if (a.DeclaringType != null)
				hash += HASHCODE_MAGIC_NESTED_TYPE;
			else
				hash += GetHashCode_TypeNamespace(a.Namespace);
			return hash;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSpec a, TypeSpec b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals(a.TypeSig, b.TypeSig);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a type
		/// </summary>
		/// <param name="a">The type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(TypeSpec a) {
			if (a == null)
				return 0;
			return GetHashCode(a.TypeSig);
		}

		/// <summary>
		/// Compares resolution scopes
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool EqualsResolutionScope(TypeRef a, TypeRef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			var ra = a.ResolutionScope;
			var rb = b.ResolutionScope;
			if (ra == rb)
				return true;
			if (ra == null || rb == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			TypeRef ea, eb;
			IModule ma, mb;
			AssemblyRef aa, ab;
			ModuleRef modRef;
			ModuleDef modDef;

			// if one of them is a TypeRef, the other one must be too
			if ((ea = ra as TypeRef) != null | (eb = rb as TypeRef) != null)
				result = Equals(ea, eb);
			else if (DontCompareTypeScope)
				result = true;
			// only compare if both are modules
			else if ((ma = ra as IModule) != null & (mb = rb as IModule) != null)
				result = Equals(ma, mb) && Equals(a.DefinitionAssembly, b.DefinitionAssembly);
			// only compare if both are assemblies
			else if ((aa = ra as AssemblyRef) != null & (ab = rb as AssemblyRef) != null)
				result = Equals((IAssembly)aa, (IAssembly)ab);
			else if (aa != null && (modRef = rb as ModuleRef) != null) {
				var bMod = b.Module;
				result = bMod != null && Equals(aa, bMod.Assembly);
			}
			else if (ab != null && (modRef = ra as ModuleRef) != null) {
				var aMod = a.Module;
				result = aMod != null && Equals(ab, aMod.Assembly);
			}
			else if (aa != null && (modDef = rb as ModuleDef) != null)
				result = Equals(aa, modDef.Assembly);
			else if (ab != null && (modDef = ra as ModuleDef) != null)
				result = Equals(ab, modDef.Assembly);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares implementation
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool EqualsImplementation(ExportedType a, ExportedType b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			var ia = a.Implementation;
			var ib = b.Implementation;
			if (ia == ib)
				return true;
			if (ia == null || ib == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			ExportedType ea, eb;
			FileDef fa, fb;
			AssemblyRef aa, ab;

			// if one of them is an ExportedType, the other one must be too
			if ((ea = ia as ExportedType) != null | (eb = ib as ExportedType) != null)
				result = Equals(ea, eb);
			else if (DontCompareTypeScope)
				result = true;
			// only compare if both are files
			else if ((fa = ia as FileDef) != null & (fb = ib as FileDef) != null)
				result = Equals(fa, fb);
			// only compare if both are assemblies
			else if ((aa = ia as AssemblyRef) != null & (ab = ib as AssemblyRef) != null)
				result = Equals((IAssembly)aa, (IAssembly)ab);
			else if (fa != null && ab != null)
				result = Equals(a.DefinitionAssembly, ab);
			else if (fb != null && aa != null)
				result = Equals(b.DefinitionAssembly, aa);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares resolution scope and implementation
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool EqualsScope(TypeRef a, ExportedType b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			var ra = a.ResolutionScope;
			var ib = b.Implementation;
			if (ra == ib)
				return true;
			if (ra == null || ib == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			TypeRef ea;
			ExportedType eb;
			IModule ma;
			FileDef fb;
			AssemblyRef aa, ab;

			// If one is a nested type, the other one must be too
			if ((ea = ra as TypeRef) != null | (eb = ib as ExportedType) != null)
				result = Equals(ea, eb);
			else if ((ma = ra as IModule) != null & (fb = ib as FileDef) != null)
				result = Equals(ma, fb) && Equals(a.DefinitionAssembly, b.DefinitionAssembly);
			else if ((aa = ra as AssemblyRef) != null & (ab = ib as AssemblyRef) != null)
				result = Equals(aa, ab);
			else if (ma != null && ab != null)
				result = Equals(a.DefinitionAssembly, ab);
			else if (fb != null && aa != null)
				result = Equals(b.DefinitionAssembly, aa);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares files
		/// </summary>
		/// <param name="a">File #1</param>
		/// <param name="b">File #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(FileDef a, FileDef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;

			return UTF8String.CaseInsensitiveEquals(a.Name, b.Name);
		}

		/// <summary>
		/// Compares a module with a file
		/// </summary>
		/// <param name="a">Module</param>
		/// <param name="b">File</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(IModule a, FileDef b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;

			//TODO: You should compare against the module's file name, not the name in the metadata!
			return UTF8String.CaseInsensitiveEquals(a.Name, b.Name);
		}

		/// <summary>
		/// Compares modules
		/// </summary>
		/// <param name="a">Module #1</param>
		/// <param name="b">Module #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		internal bool Equals(IModule a, IModule b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b))
				return true;

			return UTF8String.CaseInsensitiveEquals(a.Name, b.Name);
		}

		static bool IsCorLib(ModuleDef a) {
			return a != null && a.IsManifestModule && a.Assembly.IsCorLib();
		}

		static bool IsCorLib(IModule a) {
			var mod = a as ModuleDef;
			return mod != null && mod.IsManifestModule && mod.Assembly.IsCorLib();
		}

		static bool IsCorLib(Module a) {
			return a != null && a.Assembly.ManifestModule == a && a.Assembly == typeof(void).Assembly;
		}

		static bool IsCorLib(IAssembly a) {
			return a.IsCorLib();
		}

		static bool IsCorLib(Assembly a) {
			return a == typeof(void).Assembly;
		}

		/// <summary>
		/// Compares modules
		/// </summary>
		/// <param name="a">Module #1</param>
		/// <param name="b">Module #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(ModuleDef a, ModuleDef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b))
				return true;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals((IModule)a, (IModule)b) && Equals(a.Assembly, b.Assembly);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares assemblies
		/// </summary>
		/// <param name="a">Assembly #1</param>
		/// <param name="b">Assembly #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(IAssembly a, IAssembly b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b))
				return true;
			if (!recursionCounter.Increment())
				return false;

			bool result = UTF8String.CaseInsensitiveEquals(a.Name, b.Name) &&
				(!CompareAssemblyPublicKeyToken || PublicKeyBase.TokenEquals(a.PublicKeyOrToken, b.PublicKeyOrToken)) &&
				(!CompareAssemblyVersion || Utils.Equals(a.Version, b.Version)) &&
				(!CompareAssemblyLocale || Utils.LocaleEquals(a.Culture, b.Culture));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSig a, TypeSig b) {
			if (IgnoreModifiers) {
				a = a.RemoveModifiers();
				b = b.RemoveModifiers();
			}
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			if (a.ElementType != b.ElementType) {
				// Signatures must be identical. It's possible to have a U4 in a sig (short form
				// of System.UInt32), or a ValueType + System.UInt32 TypeRef (long form), but these
				// should not match in a sig (also the long form is invalid).
				result = false;
			}
			else {
				switch (a.ElementType) {
				case ElementType.Void:
				case ElementType.Boolean:
				case ElementType.Char:
				case ElementType.I1:
				case ElementType.U1:
				case ElementType.I2:
				case ElementType.U2:
				case ElementType.I4:
				case ElementType.U4:
				case ElementType.I8:
				case ElementType.U8:
				case ElementType.R4:
				case ElementType.R8:
				case ElementType.String:
				case ElementType.TypedByRef:
				case ElementType.I:
				case ElementType.U:
				case ElementType.Object:
				case ElementType.Sentinel:
					result = true;
					break;

				case ElementType.Ptr:
				case ElementType.ByRef:
				case ElementType.SZArray:
				case ElementType.Pinned:
					result = Equals(a.Next, b.Next);
					break;

				case ElementType.Array:
					ArraySig ara = a as ArraySig, arb = b as ArraySig;
					result = ara.Rank == arb.Rank &&
							Equals(ara.Sizes, arb.Sizes) &&
							Equals(ara.LowerBounds, arb.LowerBounds) &&
							Equals(a.Next, b.Next);
					break;

				case ElementType.ValueType:
				case ElementType.Class:
					if (RawSignatureCompare)
						result = TokenEquals((a as ClassOrValueTypeSig).TypeDefOrRef, (b as ClassOrValueTypeSig).TypeDefOrRef);
					else
						result = Equals((IType)(a as ClassOrValueTypeSig).TypeDefOrRef, (IType)(b as ClassOrValueTypeSig).TypeDefOrRef);
					break;

				case ElementType.Var:
				case ElementType.MVar:
					result = (a as GenericSig).Number == (b as GenericSig).Number;
					break;

				case ElementType.GenericInst:
					var gia = (GenericInstSig)a;
					var gib = (GenericInstSig)b;
					if (RawSignatureCompare) {
						var gt1 = gia.GenericType;
						var gt2 = gib.GenericType;
						result = TokenEquals(gt1 == null ? null : gt1.TypeDefOrRef, gt2 == null ? null : gt2.TypeDefOrRef) &&
								Equals(gia.GenericArguments, gib.GenericArguments);
					}
					else {
						result = Equals(gia.GenericType, gib.GenericType) &&
								Equals(gia.GenericArguments, gib.GenericArguments);
					}
					break;

				case ElementType.FnPtr:
					result = Equals((a as FnPtrSig).Signature, (b as FnPtrSig).Signature);
					break;

				case ElementType.CModReqd:
				case ElementType.CModOpt:
					if (RawSignatureCompare)
						result = TokenEquals((a as ModifierSig).Modifier, (b as ModifierSig).Modifier) &&
								Equals(a.Next, b.Next);
					else
						result = Equals((IType)(a as ModifierSig).Modifier, (IType)(b as ModifierSig).Modifier) &&
								Equals(a.Next, b.Next);
					break;

				case ElementType.ValueArray:
					result = (a as ValueArraySig).Size == (b as ValueArraySig).Size && Equals(a.Next, b.Next);
					break;

				case ElementType.Module:
					result = (a as ModuleSig).Index == (b as ModuleSig).Index && Equals(a.Next, b.Next);
					break;

				case ElementType.End:
				case ElementType.R:
				case ElementType.Internal:
				default:
					result = false;
					break;
				}
			}

			recursionCounter.Decrement();
			return result;
		}

		static bool TokenEquals(ITypeDefOrRef a, ITypeDefOrRef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			return a.MDToken == b.MDToken;
		}

		/// <summary>
		/// Gets the hash code of a type
		/// </summary>
		/// <param name="a">The type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(TypeSig a) {
			// ********************************************
			// IMPORTANT: This must match GetHashCode(Type)
			// ********************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			if (genericArguments != null)
				a = genericArguments.Resolve(a);

			switch (a.ElementType) {
			case ElementType.Void:
			case ElementType.Boolean:
			case ElementType.Char:
			case ElementType.I1:
			case ElementType.U1:
			case ElementType.I2:
			case ElementType.U2:
			case ElementType.I4:
			case ElementType.U4:
			case ElementType.I8:
			case ElementType.U8:
			case ElementType.R4:
			case ElementType.R8:
			case ElementType.String:
			case ElementType.TypedByRef:
			case ElementType.I:
			case ElementType.U:
			case ElementType.Object:
			case ElementType.ValueType:
			case ElementType.Class:
				// When comparing an ExportedType/TypeDef/TypeRef to a TypeDefOrRefSig/Class/ValueType,
				// the ET is ignored, so we must ignore it when calculating the hash.
				hash = GetHashCode((IType)(a as TypeDefOrRefSig).TypeDefOrRef);
				break;

			case ElementType.Sentinel:
				hash = HASHCODE_MAGIC_ET_SENTINEL;
				break;

			case ElementType.Ptr:
				hash = HASHCODE_MAGIC_ET_PTR + GetHashCode(a.Next);
				break;

			case ElementType.ByRef:
				hash = HASHCODE_MAGIC_ET_BYREF + GetHashCode(a.Next);
				break;

			case ElementType.SZArray:
				hash = HASHCODE_MAGIC_ET_SZARRAY + GetHashCode(a.Next);
				break;

			case ElementType.CModReqd:
			case ElementType.CModOpt:
			case ElementType.Pinned:
				// When comparing an ExportedType/TypeDef/TypeRef to a ModifierSig/PinnedSig,
				// the ET is ignored, so we must ignore it when calculating the hash.
				hash = GetHashCode(a.Next);
				break;

			case ElementType.Array:
				// Don't include sizes and lower bounds since GetHashCode(Type) doesn't (and can't).
				ArraySig ara = (ArraySig)a;
				hash = HASHCODE_MAGIC_ET_ARRAY + (int)ara.Rank + GetHashCode(ara.Next);
				break;

			case ElementType.Var:
				hash = HASHCODE_MAGIC_ET_VAR + (int)(a as GenericVar).Number;
				break;

			case ElementType.MVar:
				hash = HASHCODE_MAGIC_ET_MVAR + (int)(a as GenericMVar).Number;
				break;

			case ElementType.GenericInst:
				var gia = (GenericInstSig)a;
				hash = HASHCODE_MAGIC_ET_GENERICINST;
				if (SubstituteGenericParameters) {
					InitializeGenericArguments();
					genericArguments.PushTypeArgs(gia.GenericArguments);
					hash += GetHashCode(gia.GenericType);
					genericArguments.PopTypeArgs();
				}
				else
					hash += GetHashCode(gia.GenericType);
				hash += GetHashCode(gia.GenericArguments);
				break;

			case ElementType.FnPtr:
				hash = GetHashCode_FnPtr_SystemIntPtr();
				break;

			case ElementType.ValueArray:
				hash = HASHCODE_MAGIC_ET_VALUEARRAY + (int)(a as ValueArraySig).Size + GetHashCode(a.Next);
				break;

			case ElementType.Module:
				hash = HASHCODE_MAGIC_ET_MODULE + (int)(a as ModuleSig).Index + GetHashCode(a.Next);
				break;

			case ElementType.End:
			case ElementType.R:
			case ElementType.Internal:
			default:
				hash = 0;
				break;
			}

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares type lists
		/// </summary>
		/// <param name="a">Type list #1</param>
		/// <param name="b">Type list #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(IList<TypeSig> a, IList<TypeSig> b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			if (a.Count != b.Count)
				result = false;
			else {
				int i;
				for (i = 0; i < a.Count; i++) {
					if (!Equals(a[i], b[i]))
						break;
				}
				result = i == a.Count;
			}

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a type list
		/// </summary>
		/// <param name="a">The type list</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(IList<TypeSig> a) {
			//************************************************************************
			// IMPORTANT: This code must match any other GetHashCode(IList<SOME_TYPE>)
			//************************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			uint hash = 0;
			for (int i = 0; i < a.Count; i++) {
				hash += (uint)GetHashCode(a[i]);
				hash = (hash << 13) | (hash >> 19);
			}
			recursionCounter.Decrement();
			return (int)hash;
		}

		bool Equals(IList<uint> a, IList<uint> b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (a.Count != b.Count)
				return false;
			for (int i = 0; i < a.Count; i++) {
				if (a[i] != b[i])
					return false;
			}
			return true;
		}

		bool Equals(IList<int> a, IList<int> b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (a.Count != b.Count)
				return false;
			for (int i = 0; i < a.Count; i++) {
				if (a[i] != b[i])
					return false;
			}
			return true;
		}

		/// <summary>
		/// Compares signatures
		/// </summary>
		/// <param name="a">Sig #1</param>
		/// <param name="b">Sig #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(CallingConventionSig a, CallingConventionSig b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			if (a.GetCallingConvention() != b.GetCallingConvention())
				result = false;
			else {
				switch (a.GetCallingConvention() & CallingConvention.Mask) {
				case CallingConvention.Default:
				case CallingConvention.C:
				case CallingConvention.StdCall:
				case CallingConvention.ThisCall:
				case CallingConvention.FastCall:
				case CallingConvention.VarArg:
				case CallingConvention.Property:
					MethodBaseSig ma = a as MethodBaseSig, mb = b as MethodBaseSig;
					result = ma != null && mb != null && Equals(ma, mb);
					break;

				case CallingConvention.Field:
					FieldSig fa = a as FieldSig, fb = b as FieldSig;
					result = fa != null && fb != null && Equals(fa, fb);
					break;

				case CallingConvention.LocalSig:
					LocalSig la = a as LocalSig, lb = b as LocalSig;
					result = la != null && lb != null && Equals(la, lb);
					break;

				case CallingConvention.GenericInst:
					GenericInstMethodSig ga = a as GenericInstMethodSig, gb = b as GenericInstMethodSig;
					result = ga != null && gb != null && Equals(ga, gb);
					break;

				case CallingConvention.Unmanaged:
				case CallingConvention.NativeVarArg:
				default:
					result = false;
					break;
				}
			}

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a sig
		/// </summary>
		/// <param name="a">The sig</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(CallingConventionSig a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			switch (a.GetCallingConvention() & CallingConvention.Mask) {
			case CallingConvention.Default:
			case CallingConvention.C:
			case CallingConvention.StdCall:
			case CallingConvention.ThisCall:
			case CallingConvention.FastCall:
			case CallingConvention.VarArg:
			case CallingConvention.Property:
				MethodBaseSig ma = a as MethodBaseSig;
				hash = ma == null ? 0 : GetHashCode(ma);
				break;

			case CallingConvention.Field:
				FieldSig fa = a as FieldSig;
				hash = fa == null ? 0 : GetHashCode(fa);
				break;

			case CallingConvention.LocalSig:
				LocalSig la = a as LocalSig;
				hash = la == null ? 0 : GetHashCode(la);
				break;

			case CallingConvention.GenericInst:
				GenericInstMethodSig ga = a as GenericInstMethodSig;
				hash = ga == null ? 0 : GetHashCode(ga);
				break;

			case CallingConvention.Unmanaged:
			case CallingConvention.NativeVarArg:
			default:
				hash = GetHashCode_CallingConvention(a);
				break;
			}

			return hash;
		}

		/// <summary>
		/// Compares method/property sigs
		/// </summary>
		/// <param name="a">Method/property #1</param>
		/// <param name="b">Method/property #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodBaseSig a, MethodBaseSig b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = a.GetCallingConvention() == b.GetCallingConvention() &&
					(DontCompareReturnType || Equals(a.RetType, b.RetType)) &&
					Equals(a.Params, b.Params) &&
					(!a.Generic || a.GenParamCount == b.GenParamCount) &&
					(!CompareSentinelParams || Equals(a.ParamsAfterSentinel, b.ParamsAfterSentinel));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a method/property sig
		/// </summary>
		/// <param name="a">The method/property sig</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(MethodBaseSig a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			hash = GetHashCode_CallingConvention(a) +
					GetHashCode(a.Params);
			if (!DontCompareReturnType)
				hash += GetHashCode(a.RetType);
			if (a.Generic)
				hash += GetHashCode_ElementType_MVar((int)a.GenParamCount);
			if (CompareSentinelParams)
				hash += GetHashCode(a.ParamsAfterSentinel);

			recursionCounter.Decrement();
			return hash;
		}

		int GetHashCode_CallingConvention(CallingConventionSig a) {
			return GetHashCode(a.GetCallingConvention());
		}

		int GetHashCode(CallingConvention a) {
			//*******************************************************************
			// IMPORTANT: This hash must match the Reflection call conv hash code
			//*******************************************************************

			switch (a & CallingConvention.Mask) {
			case CallingConvention.Default:
			case CallingConvention.C:
			case CallingConvention.StdCall:
			case CallingConvention.ThisCall:
			case CallingConvention.FastCall:
			case CallingConvention.VarArg:
			case CallingConvention.Property:
			case CallingConvention.GenericInst:
			case CallingConvention.Unmanaged:
			case CallingConvention.NativeVarArg:
			case CallingConvention.Field:
				return (int)(a & (CallingConvention.Generic | CallingConvention.HasThis | CallingConvention.ExplicitThis));

			case CallingConvention.LocalSig:
			default:
				return (int)a;
			}
		}

		/// <summary>
		/// Compares field sigs
		/// </summary>
		/// <param name="a">Field sig #1</param>
		/// <param name="b">Field sig #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(FieldSig a, FieldSig b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = a.GetCallingConvention() == b.GetCallingConvention() && Equals(a.Type, b.Type);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a field sig
		/// </summary>
		/// <param name="a">The field sig</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(FieldSig a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			hash = GetHashCode_CallingConvention(a) + GetHashCode(a.Type);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares local sigs
		/// </summary>
		/// <param name="a">Local sig #1</param>
		/// <param name="b">Local sig #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(LocalSig a, LocalSig b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = a.GetCallingConvention() == b.GetCallingConvention() && Equals(a.Locals, b.Locals);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a local sig
		/// </summary>
		/// <param name="a">The local sig</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(LocalSig a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			hash = GetHashCode_CallingConvention(a) + GetHashCode(a.Locals);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares generic method instance sigs
		/// </summary>
		/// <param name="a">Generic inst method #1</param>
		/// <param name="b">Generic inst method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(GenericInstMethodSig a, GenericInstMethodSig b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = a.GetCallingConvention() == b.GetCallingConvention() && Equals(a.GenericArguments, b.GenericArguments);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a generic instance method sig
		/// </summary>
		/// <param name="a">The generic inst method sig</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(GenericInstMethodSig a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			hash = GetHashCode_CallingConvention(a) + GetHashCode(a.GenericArguments);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(IMethod a, IMethod b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			MethodDef mda, mdb;
			MemberRef mra, mrb;
			MethodSpec msa, msb;

			if ((mda = a as MethodDef) != null & (mdb = b as MethodDef) != null)
				result = Equals(mda, mdb);
			else if ((mra = a as MemberRef) != null & (mrb = b as MemberRef) != null)
				result = Equals(mra, mrb);
			else if ((msa = a as MethodSpec) != null && (msb = b as MethodSpec) != null)
				result = Equals(msa, msb);
			else if (mda != null && mrb != null)
				result = Equals(mda, mrb);
			else if (mra != null && mdb != null)
				result = Equals(mdb, mra);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a method
		/// </summary>
		/// <param name="a">The method</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(IMethod a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash;
			MethodDef mda;
			MemberRef mra;
			MethodSpec msa;

			if ((mda = a as MethodDef) != null)
				hash = GetHashCode(mda);
			else if ((mra = a as MemberRef) != null)
				hash = GetHashCode(mra);
			else if ((msa = a as MethodSpec) != null)
				hash = GetHashCode(msa);
			else
				hash = 0;

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MemberRef a, MethodDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodDef a, MemberRef b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = (PrivateScopeMethodIsComparable || !a.IsPrivateScope) &&
					Equals_MethodFieldNames(a.Name, b.Name) &&
					Equals(a.Signature, b.Signature) &&
					(!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.Class));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodDef a, MethodDef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_MethodFieldNames(a.Name, b.Name) &&
					Equals(a.Signature, b.Signature) &&
					(!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a method
		/// </summary>
		/// <param name="a">The method</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(MethodDef a) {
			// ***********************************************************************
			// IMPORTANT: This hash code must match the MemberRef/MethodBase hash code
			// ***********************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash = GetHashCode_MethodFieldName(a.Name) +
					GetHashCode(a.Signature);
			if (CompareMethodFieldDeclaringType)
				hash += GetHashCode(a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares <c>MemberRef</c>s
		/// </summary>
		/// <param name="a"><c>MemberRef</c> #1</param>
		/// <param name="b"><c>MemberRef</c> #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MemberRef a, MemberRef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_MethodFieldNames(a.Name, b.Name) &&
					Equals(a.Signature, b.Signature) &&
					(!CompareMethodFieldDeclaringType || Equals(a.Class, b.Class));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a <c>MemberRef</c>
		/// </summary>
		/// <param name="a">The <c>MemberRef</c></param>
		/// <returns>The hash code</returns>
		public int GetHashCode(MemberRef a) {
			// ********************************************************************************
			// IMPORTANT: This hash code must match the MethodDef/FieldDef/MethodBase hash code
			// ********************************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash = GetHashCode_MethodFieldName(a.Name);
			GenericInstSig git;
			if (SubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) != null) {
				InitializeGenericArguments();
				genericArguments.PushTypeArgs(git.GenericArguments);
				hash += GetHashCode(a.Signature);
				genericArguments.PopTypeArgs();
			}
			else
				hash += GetHashCode(a.Signature);
			if (CompareMethodFieldDeclaringType)
				hash += GetHashCode(a.Class);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares <c>MethodSpec</c>s
		/// </summary>
		/// <param name="a"><c>MethodSpec</c> #1</param>
		/// <param name="b"><c>MethodSpec</c> #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodSpec a, MethodSpec b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals(a.Method, b.Method) && Equals(a.Instantiation, b.Instantiation);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a <c>MethodSpec</c>
		/// </summary>
		/// <param name="a">The <c>MethodSpec</c></param>
		/// <returns>The hash code</returns>
		public int GetHashCode(MethodSpec a) {
			// *************************************************************
			// IMPORTANT: This hash code must match the MethodBase hash code
			// *************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			// We must do this or it won't get the same hash code as some MethodInfos
			var oldOptions = SetOptions(SigComparerOptions.SubstituteGenericParameters);
			var gim = a.GenericInstMethodSig;
			if (gim != null) {
				InitializeGenericArguments();
				genericArguments.PushMethodArgs(gim.GenericArguments);
			}
			int hash = GetHashCode(a.Method);
			if (gim != null)
				genericArguments.PopMethodArgs();
			RestoreOptions(oldOptions);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares <c>MemberRefParent</c>s
		/// </summary>
		/// <param name="a"><c>MemberRefParent</c> #1</param>
		/// <param name="b"><c>MemberRefParent</c> #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(IMemberRefParent a, IMemberRefParent b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			ITypeDefOrRef ita, itb;
			ModuleRef moda, modb;
			MethodDef ma, mb;
			TypeDef td;

			if ((ita = a as ITypeDefOrRef) != null && (itb = b as ITypeDefOrRef) != null)
				result = Equals((IType)ita, (IType)itb);
			else if ((moda = a as ModuleRef) != null & (modb = b as ModuleRef) != null) {
				ModuleDef omoda = moda.Module, omodb = modb.Module;
				result = Equals((IModule)moda, (IModule)modb) &&
						Equals(omoda == null ? null : omoda.Assembly, omodb == null ? null : omodb.Assembly);
			}
			else if ((ma = a as MethodDef) != null && (mb = b as MethodDef) != null)
				result = Equals(ma, mb);
			else if (modb != null && (td = a as TypeDef) != null)
				result = EqualsGlobal(td, modb);
			else if (moda != null && (td = b as TypeDef) != null)
				result = EqualsGlobal(td, moda);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a <c>MemberRefParent</c>
		/// </summary>
		/// <param name="a">The <c>MemberRefParent</c></param>
		/// <returns>The hash code</returns>
		int GetHashCode(IMemberRefParent a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash;
			ITypeDefOrRef ita;
			MethodDef ma;

			if ((ita = a as ITypeDefOrRef) != null)
				hash = GetHashCode((IType)ita);
			else if (a is ModuleRef)
				hash = GetHashCodeGlobalType();
			else if ((ma = a as MethodDef) != null) {
				// Only use the declaring type so we get the same hash code when hashing a MethodBase.
				hash = GetHashCode(ma.DeclaringType);
			}
			else
				hash = 0;

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(IField a, IField b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			FieldDef fa, fb;
			MemberRef ma, mb;

			if ((fa = a as FieldDef) != null & (fb = b as FieldDef) != null)
				result = Equals(fa, fb);
			else if ((ma = a as MemberRef) != null & (mb = b as MemberRef) != null)
				result = Equals(ma, mb);
			else if (fa != null && mb != null)
				result = Equals(fa, mb);
			else if (fb != null && ma != null)
				result = Equals(fb, ma);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a field
		/// </summary>
		/// <param name="a">The field</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(IField a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash;
			FieldDef fa;
			MemberRef ma;

			if ((fa = a as FieldDef) != null)
				hash = GetHashCode(fa);
			else if ((ma = a as MemberRef) != null)
				hash = GetHashCode(ma);
			else
				hash = 0;

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MemberRef a, FieldDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(FieldDef a, MemberRef b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = (PrivateScopeFieldIsComparable || !a.IsPrivateScope) &&
					Equals_MethodFieldNames(a.Name, b.Name) &&
					Equals(a.Signature, b.Signature) &&
					(!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.Class));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(FieldDef a, FieldDef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_MethodFieldNames(a.Name, b.Name) &&
					Equals(a.Signature, b.Signature) &&
					(!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a field
		/// </summary>
		/// <param name="a">The field</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(FieldDef a) {
			// **********************************************************************
			// IMPORTANT: This hash code must match the MemberRef/FieldInfo hash code
			// **********************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash = GetHashCode_MethodFieldName(a.Name) +
					GetHashCode(a.Signature);
			if (CompareMethodFieldDeclaringType)
				hash += GetHashCode(a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares properties
		/// </summary>
		/// <param name="a">Property #1</param>
		/// <param name="b">Property #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(PropertyDef a, PropertyDef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_PropertyNames(a.Name, b.Name) &&
					Equals(a.Type, b.Type) &&
					(!ComparePropertyDeclaringType || Equals(a.DeclaringType, b.DeclaringType));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a property
		/// </summary>
		/// <param name="a">The property</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(PropertyDef a) {
			// ***************************************************************
			// IMPORTANT: This hash code must match the PropertyInfo hash code
			// ***************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			var sig = a.PropertySig;
			int hash = GetHashCode_PropertyName(a.Name) +
					GetHashCode(sig == null ? null : sig.RetType);
			if (ComparePropertyDeclaringType)
				hash += GetHashCode(a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares events
		/// </summary>
		/// <param name="a">Event #1</param>
		/// <param name="b">Event #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(EventDef a, EventDef b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_EventNames(a.Name, b.Name) &&
					Equals((IType)a.EventType, (IType)b.EventType) &&
					(!CompareEventDeclaringType || Equals(a.DeclaringType, b.DeclaringType));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of an event
		/// </summary>
		/// <param name="a">The event</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(EventDef a) {
			// ************************************************************
			// IMPORTANT: This hash code must match the EventInfo hash code
			// ************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash = GetHashCode_EventName(a.Name) +
					GetHashCode((IType)a.EventType);
			if (CompareEventDeclaringType)
				hash += GetHashCode(a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		// Compares a with b, and a must be the global type
		bool EqualsGlobal(TypeDef a, ModuleRef b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = a.IsGlobalModuleType &&
				Equals((IModule)a.Module, (IModule)b) &&
				Equals(a.DefinitionAssembly, b.Module == null ? null : b.Module.Assembly);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(Type a, IType b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(IType a, Type b) {
			// Global methods and fields have their DeclaringType set to null. Assume
			// null always means the global type.
			if (a == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			TypeDef td;
			TypeRef tr;
			TypeSpec ts;
			TypeSig sig;
			ExportedType et;

			if ((td = a as TypeDef) != null)
				result = Equals(td, b);
			else if ((tr = a as TypeRef) != null)
				result = Equals(tr, b);
			else if ((ts = a as TypeSpec) != null)
				result = Equals(ts, b);
			else if ((sig = a as TypeSig) != null)
				result = Equals(sig, b);
			else if ((et = a as ExportedType) != null)
				result = Equals(et, b);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(Type a, TypeDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeDef a, Type b) {
			// Global methods and fields have their DeclaringType set to null. Assume
			// null always means the global type.
			if (a == null)
				return false;
			if (b == null)
				return a.IsGlobalModuleType;
			if (!recursionCounter.Increment())
				return false;

			bool result = !b.HasElementType &&
					Equals_TypeNames(a.Name, b.Name) &&
					Equals_TypeNamespaces(a.Namespace, b) &&
					EnclosingTypeEquals(a.DeclaringType, b.DeclaringType) &&
					(DontCompareTypeScope || Equals(a.Module, b.Module));

			recursionCounter.Decrement();
			return result;
		}

		bool EnclosingTypeEquals(TypeDef a, Type b) {
			// b == null doesn't mean that b is the global type
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			return Equals(a, b);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(Type a, TypeRef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="b">Type #1</param>
		/// <param name="a">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeRef a, Type b) {
			// Global methods and fields have their DeclaringType set to null. Assume
			// null always means the global type.
			if (a == null)
				return false;
			if (b == null)
				return false;	// Must use a ModuleRef to reference the global type, so always fail
			if (!recursionCounter.Increment())
				return false;

			bool result;
			TypeRef dta;
			IModule aMod;
			AssemblyRef aAsm;
			var scope = a.ResolutionScope;

			if (!b.IsTypeDef())
				result = false;
			else if (!Equals_TypeNames(a.Name, b.Name) || !Equals_TypeNamespaces(a.Namespace, b))
				result = false;
			else if ((dta = scope as TypeRef) != null)	// nested type
				result = Equals(dta, b.DeclaringType);	// Compare enclosing types
			else if (b.IsNested)
				result = false;	// b is nested, a isn't
			else if (DontCompareTypeScope)
				result = true;
			else if ((aMod = scope as IModule) != null) {	// 'a' is defined in the same assembly as 'b'
				result = Equals(aMod, b.Module) &&
						Equals(a.DefinitionAssembly, b.Assembly);
			}
			else if ((aAsm = scope as AssemblyRef) != null)
				result = Equals(aAsm, b.Assembly);
			else {
				result = false;
				//TODO: Handle the case where scope == null
			}

			recursionCounter.Decrement();
			return result;
		}

		bool Equals_TypeNamespaces(UTF8String a, Type b) {
			if (b.IsNested)
				return true;
			return Equals_TypeNamespaces(a, b.Namespace);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(Type a, TypeSpec b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSpec a, Type b) {
			// Global methods and fields have their DeclaringType set to null. Assume
			// null always means the global type.
			if (a == null)
				return false;
			if (b == null)
				return false;	// Must use a ModuleRef to reference the global type, so always fail
			return Equals(a.TypeSig, b);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(Type a, TypeSig b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(TypeSig a, Type b) {
			return Equals(a, b, false);
		}

		bool Equals(ITypeDefOrRef a, Type b, bool treatAsGenericInst) {
			var ts = a as TypeSpec;
			if (ts != null)
				return Equals(ts.TypeSig, b, treatAsGenericInst);
			return Equals(a, b);
		}

		/// <summary>
		/// Checks whether it's FnPtr&amp;, FnPtr*, FnPtr[], or FnPtr[...]
		/// </summary>
		/// <param name="a">The type</param>
		static bool IsFnPtrElementType(Type a) {
			if (a == null || !a.HasElementType)
				return false;
			var et = a.GetElementType();
			if (et == null || et.HasElementType)
				return false;
			if (et != typeof(IntPtr))	// FnPtr is mapped to System.IntPtr
				return false;
			if (!a.FullName.StartsWith("(fnptr)"))
				return false;

			return true;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <param name="treatAsGenericInst"><c>true</c> if we should treat <paramref name="b"/>
		/// as a generic instance type</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(TypeSig a, Type b, bool treatAsGenericInst) {
			// Global methods and fields have their DeclaringType set to null. Assume
			// null always means the global type.
			if (a == null)
				return false;
			if (b == null)
				return false;	// Must use a ModuleRef to reference the global type, so always fail
			if (!recursionCounter.Increment())
				return false;
			bool result;

			if (genericArguments != null)
				a = genericArguments.Resolve(a);

			switch (a.ElementType) {
			case ElementType.Void:
			case ElementType.Boolean:
			case ElementType.Char:
			case ElementType.I1:
			case ElementType.U1:
			case ElementType.I2:
			case ElementType.U2:
			case ElementType.I4:
			case ElementType.U4:
			case ElementType.I8:
			case ElementType.U8:
			case ElementType.R4:
			case ElementType.R8:
			case ElementType.String:
			case ElementType.TypedByRef:
			case ElementType.I:
			case ElementType.U:
			case ElementType.Object:
				result = Equals(((TypeDefOrRefSig)a).TypeDefOrRef, b, treatAsGenericInst);
				break;

			case ElementType.Ptr:
				if (!b.IsPointer)
					result = false;
				else if (IsFnPtrElementType(b)) {
					a = a.Next.RemoveModifiers();
					result = a != null && a.ElementType == ElementType.FnPtr;
				}
				else
					result = Equals(a.Next, b.GetElementType());
				break;

			case ElementType.ByRef:
				if (!b.IsByRef)
					result = false;
				else if (IsFnPtrElementType(b)) {
					a = a.Next.RemoveModifiers();
					result = a != null && a.ElementType == ElementType.FnPtr;
				}
				else
					result = Equals(a.Next, b.GetElementType());
				break;

			case ElementType.SZArray:
				if (!b.IsArray || !b.IsSZArray())
					result = false;
				else if (IsFnPtrElementType(b)) {
					a = a.Next.RemoveModifiers();
					result = a != null && a.ElementType == ElementType.FnPtr;
				}
				else
					result = Equals(a.Next, b.GetElementType());
				break;

			case ElementType.Pinned:
				result = Equals(a.Next, b, treatAsGenericInst);
				break;

			case ElementType.Array:
				if (!b.IsArray || b.IsSZArray())
					result = false;
				else {
					ArraySig ara = a as ArraySig;
					result = ara.Rank == b.GetArrayRank() &&
						(IsFnPtrElementType(b) ?
								(a = a.Next.RemoveModifiers()) != null && a.ElementType == ElementType.FnPtr :
								Equals(a.Next, b.GetElementType()));
				}
				break;

			case ElementType.ValueType:
			case ElementType.Class:
				result = Equals((a as ClassOrValueTypeSig).TypeDefOrRef, b, treatAsGenericInst);
				break;

			case ElementType.Var:
				result = b.IsGenericParameter &&
						b.GenericParameterPosition == (a as GenericSig).Number &&
						b.DeclaringMethod == null;
				break;

			case ElementType.MVar:
				result = b.IsGenericParameter &&
						b.GenericParameterPosition == (a as GenericSig).Number &&
						b.DeclaringMethod != null;
				break;

			case ElementType.GenericInst:
				if (!(b.IsGenericType && !b.IsGenericTypeDefinition) && !treatAsGenericInst) {
					result = false;
					break;
				}
				var gia = (GenericInstSig)a;
				if (SubstituteGenericParameters) {
					InitializeGenericArguments();
					genericArguments.PushTypeArgs(gia.GenericArguments);
					result = Equals(gia.GenericType, b.GetGenericTypeDefinition());
					genericArguments.PopTypeArgs();
				}
				else
					result = Equals(gia.GenericType, b.GetGenericTypeDefinition());
				result = result && Equals(gia.GenericArguments, b.GetGenericArguments());
				break;

			case ElementType.CModReqd:
			case ElementType.CModOpt:
				result = Equals(a.Next, b, treatAsGenericInst);
				break;

			case ElementType.FnPtr:
				// At least in method sigs, this will be mapped to System.IntPtr
				result = b == typeof(IntPtr);
				break;

			case ElementType.Sentinel:
			case ElementType.ValueArray:
			case ElementType.Module:
			case ElementType.End:
			case ElementType.R:
			case ElementType.Internal:
			default:
				result = false;
				break;
			}

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="a">Type #1</param>
		/// <param name="b">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(Type a, ExportedType b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares types
		/// </summary>
		/// <param name="b">Type #1</param>
		/// <param name="a">Type #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(ExportedType a, Type b) {
			// Global methods and fields have their DeclaringType set to null. Assume
			// null always means the global type.
			if (a == null)
				return false;
			if (b == null)
				return false;	// Must use a ModuleRef to reference the global type, so always fail
			if (!recursionCounter.Increment())
				return false;

			bool result;
			ExportedType dta;
			FileDef aFile;
			AssemblyRef aAsm;
			var scope = a.Implementation;

			if (!b.IsTypeDef())
				result = false;
			else if (!Equals_TypeNames(a.TypeName, b.Name) || !Equals_TypeNamespaces(a.TypeNamespace, b))
				result = false;
			else if ((dta = scope as ExportedType) != null)	// nested type
				result = Equals(dta, b.DeclaringType);	// Compare enclosing types
			else if (b.IsNested)
				result = false;	// b is nested, a isn't
			else if (DontCompareTypeScope)
				result = true;
			else if ((aFile = scope as FileDef) != null) {
				result = Equals(aFile, b.Module) &&
						Equals(a.DefinitionAssembly, b.Assembly);
			}
			else if ((aAsm = scope as AssemblyRef) != null)
				result = Equals(aAsm, b.Assembly);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a type
		/// </summary>
		/// <param name="a">The type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(Type a) {
			return GetHashCode(a, false);
		}

		/// <summary>
		/// Gets the hash code of a type
		/// </summary>
		/// <param name="a">The type</param>
		/// <param name="treatAsGenericInst"><c>true</c> if we should treat <paramref name="a"/>
		/// as a generic instance type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(Type a, bool treatAsGenericInst) {
			// **************************************************************************
			// IMPORTANT: This hash code must match the TypeSig/TypeDef/TypeRef hash code
			// **************************************************************************
			if (a == null)	// Could be global type
				return GetHashCode_TypeDef(a);
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			switch (treatAsGenericInst ? ElementType.GenericInst : a.GetElementType2()) {
			case ElementType.Void:
			case ElementType.Boolean:
			case ElementType.Char:
			case ElementType.I1:
			case ElementType.U1:
			case ElementType.I2:
			case ElementType.U2:
			case ElementType.I4:
			case ElementType.U4:
			case ElementType.I8:
			case ElementType.U8:
			case ElementType.R4:
			case ElementType.R8:
			case ElementType.String:
			case ElementType.TypedByRef:
			case ElementType.I:
			case ElementType.U:
			case ElementType.Object:
			case ElementType.ValueType:
			case ElementType.Class:
				hash = GetHashCode_TypeDef(a);
				break;

			case ElementType.FnPtr:
				hash = GetHashCode_FnPtr_SystemIntPtr();
				break;

			case ElementType.Sentinel:
				hash = HASHCODE_MAGIC_ET_SENTINEL;
				break;

			case ElementType.Ptr:
				hash = HASHCODE_MAGIC_ET_PTR +
					(IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType()));
				break;

			case ElementType.ByRef:
				hash = HASHCODE_MAGIC_ET_BYREF +
					(IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType()));
				break;

			case ElementType.SZArray:
				hash = HASHCODE_MAGIC_ET_SZARRAY +
					(IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType()));
				break;

			case ElementType.CModReqd:
			case ElementType.CModOpt:
			case ElementType.Pinned:
				hash = GetHashCode(a.GetElementType());
				break;

			case ElementType.Array:
				// The type doesn't store sizes and lower bounds, so can't use them to
				// create the hash
				hash = HASHCODE_MAGIC_ET_ARRAY + a.GetArrayRank() +
					(IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType()));
				break;

			case ElementType.Var:
				hash = HASHCODE_MAGIC_ET_VAR + a.GenericParameterPosition;
				break;

			case ElementType.MVar:
				hash = HASHCODE_MAGIC_ET_MVAR + a.GenericParameterPosition;
				break;

			case ElementType.GenericInst:
				hash = HASHCODE_MAGIC_ET_GENERICINST + GetHashCode(a.GetGenericTypeDefinition()) + GetHashCode(a.GetGenericArguments());
				break;

			case ElementType.ValueArray:
			case ElementType.Module:
			case ElementType.End:
			case ElementType.R:
			case ElementType.Internal:
			default:
				hash = 0;
				break;
			}

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Gets the hash code of a type list
		/// </summary>
		/// <param name="a">The type list</param>
		/// <returns>The hash code</returns>
		int GetHashCode(IList<Type> a) {
			//************************************************************************
			// IMPORTANT: This code must match any other GetHashCode(IList<SOME_TYPE>)
			//************************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			uint hash = 0;
			for (int i = 0; i < a.Count; i++) {
				hash += (uint)GetHashCode(a[i]);
				hash = (hash << 13) | (hash >> 19);
			}
			recursionCounter.Decrement();
			return (int)hash;
		}

		/// <summary>
		/// Gets the hash code of a list with only generic method parameters (<see cref="ElementType.MVar"/>)
		/// </summary>
		/// <param name="numGenericParams">Number of generic method parameters</param>
		/// <returns>Hash code</returns>
		static int GetHashCode_ElementType_MVar(int numGenericParams) {
			return GetHashCode(numGenericParams, HASHCODE_MAGIC_ET_MVAR);
		}

		static int GetHashCode(int numGenericParams, int etypeHashCode) {
			//************************************************************************
			// IMPORTANT: This code must match any other GetHashCode(IList<SOME_TYPE>)
			//************************************************************************
			uint hash = 0;
			for (int i = 0; i < numGenericParams; i++) {
				hash += (uint)(etypeHashCode + i);
				hash = (hash << 13) | (hash >> 19);
			}
			return (int)hash;
		}

		/// <summary>
		/// Gets the hash code of a TypeDef type
		/// </summary>
		/// <param name="a">The type</param>
		/// <returns>The hash code</returns>
		public int GetHashCode_TypeDef(Type a) {
			// ************************************************************************************
			// IMPORTANT: This hash code must match the Type/TypeRef/TypeDef/ExportedType
			// hash code and GetHashCode_FnPtr_SystemIntPtr() method
			// ************************************************************************************

			// A global method/field's declaring type is null. This is the reason we must
			// return GetHashCodeGlobalType() here.
			if (a == null)
				return GetHashCodeGlobalType();
			int hash;
			hash = GetHashCode_TypeName(a.Name);
			if (a.IsNested)
				hash += HASHCODE_MAGIC_NESTED_TYPE;
			else
				hash += GetHashCode_TypeNamespace(a.Namespace);
			return hash;
		}

		/// <summary>
		/// Compares type lists
		/// </summary>
		/// <param name="a">Type list #1</param>
		/// <param name="b">Type list #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(IList<TypeSig> a, IList<Type> b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			if (a.Count != b.Count)
				result = false;
			else {
				int i;
				for (i = 0; i < a.Count; i++) {
					if (!Equals(a[i], b[i]))
						break;
				}
				result = i == a.Count;
			}

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares modules
		/// </summary>
		/// <param name="a">Module #1</param>
		/// <param name="b">Module #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(ModuleDef a, Module b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b))
				return true;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals((IModule)a, b) && Equals(a.Assembly, b.Assembly);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares a file and a module
		/// </summary>
		/// <param name="a">File</param>
		/// <param name="b">Module</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(FileDef a, Module b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;

			// Use b.Name since it's the filename we want to compare, not b.ScopeName
			return UTF8String.ToSystemStringOrEmpty(a.Name).Equals(b.Name, StringComparison.OrdinalIgnoreCase);
		}

		/// <summary>
		/// Compares modules
		/// </summary>
		/// <param name="a">Module #1</param>
		/// <param name="b">Module #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		internal bool Equals(IModule a, Module b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b))
				return true;

			// Use b.ScopeName and not b.Name since b.Name is just the file name w/o path
			return UTF8String.ToSystemStringOrEmpty(a.Name).Equals(b.ScopeName, StringComparison.OrdinalIgnoreCase);
		}

		/// <summary>
		/// Compares assemblies
		/// </summary>
		/// <param name="a">Assembly #1</param>
		/// <param name="b">Assembly #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(IAssembly a, Assembly b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b))
				return true;
			if (!recursionCounter.Increment())
				return false;

			var bAsmName = b.GetName();
			bool result = UTF8String.ToSystemStringOrEmpty(a.Name).Equals(bAsmName.Name, StringComparison.OrdinalIgnoreCase) &&
				(!CompareAssemblyPublicKeyToken || PublicKeyBase.TokenEquals(a.PublicKeyOrToken, new PublicKeyToken(bAsmName.GetPublicKeyToken()))) &&
				(!CompareAssemblyVersion || Utils.Equals(a.Version, bAsmName.Version)) &&
				(!CompareAssemblyLocale || Utils.LocaleEquals(a.Culture, bAsmName.CultureInfo.Name));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares method declaring types
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool DeclaringTypeEquals(IMethod a, MethodBase b) {
			// If this is disabled, always return true, even if one is null, etc.
			if (!CompareMethodFieldDeclaringType)
				return true;

			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			MethodDef md;
			MemberRef mr;
			MethodSpec ms;

			if ((md = a as MethodDef) != null)
				result = DeclaringTypeEquals(md, b);
			else if ((mr = a as MemberRef) != null)
				result = DeclaringTypeEquals(mr, b);
			else if ((ms = a as MethodSpec) != null)
				result = DeclaringTypeEquals(ms, b);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		bool DeclaringTypeEquals(MethodDef a, MethodBase b) {
			// If this is disabled, always return true, even if one is null, etc.
			if (!CompareMethodFieldDeclaringType)
				return true;
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			return Equals(a.DeclaringType, b.DeclaringType);
		}

		bool DeclaringTypeEquals(MemberRef a, MethodBase b) {
			// If this is disabled, always return true, even if one is null, etc.
			if (!CompareMethodFieldDeclaringType)
				return true;
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			return Equals(a.Class, b.DeclaringType, b.Module);
		}

		bool DeclaringTypeEquals(MethodSpec a, MethodBase b) {
			// If this is disabled, always return true, even if one is null, etc.
			if (!CompareMethodFieldDeclaringType)
				return true;
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			return DeclaringTypeEquals(a.Method, b);
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodBase a, IMethod b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(IMethod a, MethodBase b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			MethodDef md;
			MemberRef mr;
			MethodSpec ms;

			if ((md = a as MethodDef) != null)
				result = Equals(md, b);
			else if ((mr = a as MemberRef) != null)
				result = Equals(mr, b);
			else if ((ms = a as MethodSpec) != null)
				result = Equals(ms, b);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodBase a, MethodDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodDef a, MethodBase b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			var amSig = a.MethodSig;
			bool result = Equals_MethodFieldNames(a.Name, b.Name) &&
					amSig != null &&
					((amSig.Generic && b.IsGenericMethodDefinition && b.IsGenericMethod) ||
					(!amSig.Generic && !b.IsGenericMethodDefinition && !b.IsGenericMethod)) &&
					Equals(amSig, b) &&
					(!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares method sigs
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodBase a, MethodSig b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares method sigs
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodSig a, MethodBase b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals(a.GetCallingConvention(), b) &&
					(DontCompareReturnType || ReturnTypeEquals(a.RetType, b)) &&
					Equals(a.Params, b.GetParameters(), b.DeclaringType) &&
					(!a.Generic || a.GenParamCount == b.GetGenericArguments().Length);

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodBase a, MemberRef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MemberRef a, MethodBase b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			if (b.IsGenericMethod && !b.IsGenericMethodDefinition) {
				// 'a' must be a method ref in a generic type. This comparison must match
				// the MethodSpec vs MethodBase comparison code.
				result = a.IsMethodRef && a.MethodSig.Generic;

				var oldOptions = ClearOptions(SigComparerOptions.CompareMethodFieldDeclaringType);
				result = result && Equals(a, b.Module.ResolveMethod(b.MetadataToken));
				RestoreOptions(oldOptions);
				result = result && DeclaringTypeEquals(a, b);

				result = result && GenericMethodArgsEquals((int)a.MethodSig.GenParamCount, b.GetGenericArguments());
			}
			else {
				var amSig = a.MethodSig;
				result = Equals_MethodFieldNames(a.Name, b.Name) &&
						amSig != null &&
						((amSig.Generic && b.IsGenericMethodDefinition && b.IsGenericMethod) ||
						(!amSig.Generic && !b.IsGenericMethodDefinition && !b.IsGenericMethod));

				GenericInstSig git;
				if (SubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) != null) {
					InitializeGenericArguments();
					genericArguments.PushTypeArgs(git.GenericArguments);
					result = result && Equals(amSig, b);
					genericArguments.PopTypeArgs();
				}
				else
					result = result && Equals(amSig, b);

				result = result && (!CompareMethodFieldDeclaringType || Equals(a.Class, b.DeclaringType, b.Module));
			}

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares generic method args, making sure <paramref name="methodGenArgs"/> only
		/// contains <see cref="ElementType.MVar"/>s.
		/// </summary>
		/// <param name="numMethodArgs">Number of generic method args in method #1</param>
		/// <param name="methodGenArgs">Generic method args in method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		static bool GenericMethodArgsEquals(int numMethodArgs, IList<Type> methodGenArgs) {
			if (numMethodArgs != methodGenArgs.Count)
				return false;
			for (int i = 0; i < numMethodArgs; i++) {
				if (methodGenArgs[i].GetElementType2() != ElementType.MVar)
					return false;
			}
			return true;
		}

		bool Equals(IMemberRefParent a, Type b, Module bModule) {
			// Global methods and fields have their DeclaringType set to null. Assume
			// null always means the global type.
			if (a == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			ITypeDefOrRef ita;
			ModuleRef moda;
			MethodDef ma;
			TypeDef td;

			if ((ita = a as ITypeDefOrRef) != null)
				result = Equals((IType)ita, b);
			else if ((moda = a as ModuleRef) != null) {
				ModuleDef omoda = moda.Module;
				result = b == null &&	// b == null => it's the global type
						Equals(moda, bModule) &&
						Equals(omoda == null ? null : omoda.Assembly, bModule.Assembly);
			}
			else if ((ma = a as MethodDef) != null)
				result = Equals(ma.DeclaringType, b);
			else if (b == null && (td = a as TypeDef) != null)
				result = td.IsGlobalModuleType;
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodBase a, MethodSpec b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares methods
		/// </summary>
		/// <param name="a">Method #1</param>
		/// <param name="b">Method #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MethodSpec a, MethodBase b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			// Make sure it's a MethodSpec
			bool result = b.IsGenericMethod && !b.IsGenericMethodDefinition;

			// Don't compare declaring types yet because the resolved method has the wrong
			// declaring type (its declaring type is a generic type def).
			// NOTE: We must not push generic method args when comparing a.Method
			var oldOptions = ClearOptions(SigComparerOptions.CompareMethodFieldDeclaringType);
			result = result && Equals(a.Method, b.Module.ResolveMethod(b.MetadataToken));
			RestoreOptions(oldOptions);
			result = result && DeclaringTypeEquals(a.Method, b);

			var gim = a.GenericInstMethodSig;
			result = result && gim != null && Equals(gim.GenericArguments, b.GetGenericArguments());

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a <c>MethodBase</c>
		/// </summary>
		/// <param name="a">The <c>MethodBase</c></param>
		/// <returns>The hash code</returns>
		public int GetHashCode(MethodBase a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			// ***********************************************************************
			// IMPORTANT: This hash code must match the MemberRef/MethodSpec hash code
			// ***********************************************************************
			int hash = GetHashCode_MethodFieldName(a.Name) +
					GetHashCode_MethodSig(a);
			if (CompareMethodFieldDeclaringType)
				hash += GetHashCode(a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		int GetHashCode_MethodSig(MethodBase a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			hash = GetHashCode_CallingConvention(a.CallingConvention, a.IsGenericMethod) +
					GetHashCode(a.GetParameters(), a.DeclaringType);
			if (!DontCompareReturnType)
				hash += GetHashCode_ReturnType(a);
			if (a.IsGenericMethod)
				hash += GetHashCode_ElementType_MVar(a.GetGenericArguments().Length);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Gets the hash code of a parameter list
		/// </summary>
		/// <param name="a">The type list</param>
		/// <param name="declaringType">Declaring type of method that owns parameter <paramref name="a"/></param>
		/// <returns>The hash code</returns>
		int GetHashCode(IList<ParameterInfo> a, Type declaringType) {
			//************************************************************************
			// IMPORTANT: This code must match any other GetHashCode(IList<SOME_TYPE>)
			//************************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			uint hash = 0;
			for (int i = 0; i < a.Count; i++) {
				hash += (uint)GetHashCode(a[i], declaringType);
				hash = (hash << 13) | (hash >> 19);
			}
			recursionCounter.Decrement();
			return (int)hash;
		}

		int GetHashCode_ReturnType(MethodBase a) {
			var mi = a as MethodInfo;
			if (mi != null)
				return GetHashCode(mi.ReturnParameter, a.DeclaringType);
			return GetHashCode(typeof(void));
		}

		int GetHashCode(ParameterInfo a, Type declaringType) {
			return GetHashCode(a.ParameterType, declaringType.MustTreatTypeAsGenericInstType(a.ParameterType));
		}

		int GetHashCode(Type a, Type declaringType) {
			return GetHashCode(a, declaringType.MustTreatTypeAsGenericInstType(a));
		}

		/// <summary>
		/// Compares calling conventions
		/// </summary>
		/// <param name="a">Calling convention</param>
		/// <param name="b">Method</param>
		/// <returns></returns>
		static bool Equals(CallingConvention a, MethodBase b) {
			var bc = b.CallingConvention;

			if (((a & CallingConvention.Generic) != 0) != b.IsGenericMethod)
				return false;
			if (((a & CallingConvention.HasThis) != 0) != ((bc & CallingConventions.HasThis) != 0))
				return false;
			if (((a & CallingConvention.ExplicitThis) != 0) != ((bc & CallingConventions.ExplicitThis) != 0))
				return false;

			var cca = a & CallingConvention.Mask;
			switch (bc & CallingConventions.Any) {
			case CallingConventions.Standard:
				if (cca == CallingConvention.VarArg || cca == CallingConvention.NativeVarArg)
					return false;
				break;

			case CallingConventions.VarArgs:
				if (cca != CallingConvention.VarArg && cca != CallingConvention.NativeVarArg)
					return false;
				break;

			case CallingConventions.Any:
			default:
				break;
			}

			return true;
		}

		static int GetHashCode_CallingConvention(CallingConventions a, bool isGeneric) {
			//**************************************************************
			// IMPORTANT: This hash must match the other call conv hash code
			//**************************************************************

			CallingConvention cc = 0;

			if (isGeneric)
				cc |= CallingConvention.Generic;
			if ((a & CallingConventions.HasThis) != 0)
				cc |= CallingConvention.HasThis;
			if ((a & CallingConventions.ExplicitThis) != 0)
				cc |= CallingConvention.ExplicitThis;

			return (int)cc;
		}

		/// <summary>
		/// Compares return types
		/// </summary>
		/// <param name="a">Return type #1</param>
		/// <param name="b">MethodBase</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool ReturnTypeEquals(TypeSig a, MethodBase b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			var mi = b as MethodInfo;
			if (mi != null)
				result = Equals(a, mi.ReturnParameter, b.DeclaringType);
			else if (b is ConstructorInfo)
				result = IsSystemVoid(a);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		static bool IsSystemVoid(TypeSig a) {
			return a != null && a.FullName == "System.Void" && a.DefinitionAssembly.IsCorLib();
		}

		/// <summary>
		/// Compares parameter lists
		/// </summary>
		/// <param name="a">Type list #1</param>
		/// <param name="b">Type list #2</param>
		/// <param name="declaringType">Declaring type of method that owns parameter <paramref name="b"/></param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(IList<TypeSig> a, IList<ParameterInfo> b, Type declaringType) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			if (a.Count != b.Count)
				result = false;
			else {
				int i;
				for (i = 0; i < a.Count; i++) {
					if (!Equals(a[i], b[i], declaringType))
						break;
				}
				result = i == a.Count;
			}

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares parameter types
		/// </summary>
		/// <param name="a">Parameter type #1</param>
		/// <param name="b">Parameter #2</param>
		/// <param name="declaringType">Declaring type of method that owns parameter <paramref name="b"/></param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		bool Equals(TypeSig a, ParameterInfo b, Type declaringType) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			TypeSig a2;
			bool result = ModifiersEquals(a, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out a2) &&
						Equals(a2, b.ParameterType, declaringType.MustTreatTypeAsGenericInstType(b.ParameterType));

			recursionCounter.Decrement();
			return result;
		}

		bool ModifiersEquals(TypeSig a, IList<Type> reqMods2, IList<Type> optMods2, out TypeSig aAfterModifiers) {
			aAfterModifiers = a;
			if (!(a is ModifierSig))
				return reqMods2.Count == 0 && optMods2.Count == 0;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			var reqMods1 = new List<ITypeDefOrRef>(reqMods2.Count);
			var optMods1 = new List<ITypeDefOrRef>(optMods2.Count);
			while (true) {
				var modifierSig = aAfterModifiers as ModifierSig;
				if (modifierSig == null)
					break;
				if (modifierSig is CModOptSig)
					optMods1.Add(modifierSig.Modifier);
				else
					reqMods1.Add(modifierSig.Modifier);

				// This can only loop forever if the user created a loop. It's not possible
				// to create a loop with invalid metadata.
				aAfterModifiers = aAfterModifiers.Next;
			}
			optMods1.Reverse();
			reqMods1.Reverse();

			result = reqMods1.Count == reqMods2.Count &&
					optMods1.Count == optMods2.Count &&
					ModifiersEquals(reqMods1, reqMods2) &&
					ModifiersEquals(optMods1, optMods2);

			recursionCounter.Decrement();
			return result;
		}

		bool ModifiersEquals(IList<ITypeDefOrRef> a, IList<Type> b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;
			bool result;

			if (a.Count != b.Count)
				result = false;
			else {
				int i;
				for (i = 0; i < b.Count; i++) {
					if (!Equals(a[i], b[i]))
						break;
				}
				result = i == b.Count;
			}

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(FieldInfo a, IField b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(IField a, FieldInfo b) {
			if (a == b)
				return true;
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result;
			FieldDef fa;
			MemberRef ma;

			if ((fa = a as FieldDef) != null)
				result = Equals(fa, b);
			else if ((ma = a as MemberRef) != null)
				result = Equals(ma, b);
			else
				result = false;

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(FieldInfo a, FieldDef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(FieldDef a, FieldInfo b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_MethodFieldNames(a.Name, b.Name) &&
					Equals(a.FieldSig, b) &&
					(!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType));

			recursionCounter.Decrement();
			return result;
		}

		bool Equals(FieldSig a, FieldInfo b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			TypeSig a2;
			bool result = ModifiersEquals(a.Type, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out a2) &&
					Equals(a2, b.FieldType, b.DeclaringType.MustTreatTypeAsGenericInstType(b.FieldType));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(FieldInfo a, MemberRef b) {
			return Equals(b, a);
		}

		/// <summary>
		/// Compares fields
		/// </summary>
		/// <param name="a">Field #1</param>
		/// <param name="b">Field #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(MemberRef a, FieldInfo b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_MethodFieldNames(a.Name, b.Name);

			GenericInstSig git;
			if (SubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) != null) {
				InitializeGenericArguments();
				genericArguments.PushTypeArgs(git.GenericArguments);
				result = result && Equals(a.FieldSig, b);
				genericArguments.PopTypeArgs();
			}
			else
				result = result && Equals(a.FieldSig, b);

			result = result && (!CompareMethodFieldDeclaringType || Equals(a.Class, b.DeclaringType, b.Module));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a field
		/// </summary>
		/// <param name="a">The field</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(FieldInfo a) {
			// ************************************************************
			// IMPORTANT: This hash code must match the MemberRef hash code
			// ************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash = GetHashCode_MethodFieldName(a.Name) +
					GetHashCode_FieldSig(a);
			if (CompareMethodFieldDeclaringType)
				hash += GetHashCode(a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		int GetHashCode_FieldSig(FieldInfo a) {
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;
			int hash;

			hash = GetHashCode_CallingConvention(0, false) + GetHashCode(a.FieldType, a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares properties
		/// </summary>
		/// <param name="a">Property #1</param>
		/// <param name="b">Property #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(PropertyDef a, PropertyInfo b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_PropertyNames(a.Name, b.Name) &&
					Equals(a.PropertySig, b) &&
					(!ComparePropertyDeclaringType || Equals(a.DeclaringType, b.DeclaringType));

			recursionCounter.Decrement();
			return result;
		}

		bool Equals(PropertySig a, PropertyInfo b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			TypeSig a2;
			bool result = ModifiersEquals(a.RetType, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out a2) &&
					Equals(a2, b.PropertyType, b.DeclaringType.MustTreatTypeAsGenericInstType(b.PropertyType));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of a property
		/// </summary>
		/// <param name="a">The property</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(PropertyInfo a) {
			// **************************************************************
			// IMPORTANT: This hash code must match the PropertyDef hash code
			// **************************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash = GetHashCode_PropertyName(a.Name) +
					GetHashCode(a.PropertyType, a.DeclaringType);
			if (ComparePropertyDeclaringType)
				hash += GetHashCode(a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		/// <summary>
		/// Compares events
		/// </summary>
		/// <param name="a">Event #1</param>
		/// <param name="b">Event #2</param>
		/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
		public bool Equals(EventDef a, EventInfo b) {
			if ((object)a == (object)b)
				return true;	// both are null
			if (a == null || b == null)
				return false;
			if (!recursionCounter.Increment())
				return false;

			bool result = Equals_EventNames(a.Name, b.Name) &&
					Equals(a.EventType, b.EventHandlerType, b.DeclaringType.MustTreatTypeAsGenericInstType(b.EventHandlerType)) &&
					(!CompareEventDeclaringType || Equals(a.DeclaringType, b.DeclaringType));

			recursionCounter.Decrement();
			return result;
		}

		/// <summary>
		/// Gets the hash code of an event
		/// </summary>
		/// <param name="a">The event</param>
		/// <returns>The hash code</returns>
		public int GetHashCode(EventInfo a) {
			// ***********************************************************
			// IMPORTANT: This hash code must match the EventDef hash code
			// ***********************************************************
			if (a == null)
				return 0;
			if (!recursionCounter.Increment())
				return 0;

			int hash = GetHashCode_EventName(a.Name) +
					GetHashCode(a.EventHandlerType, a.DeclaringType);
			if (CompareEventDeclaringType)
				hash += GetHashCode(a.DeclaringType);

			recursionCounter.Decrement();
			return hash;
		}

		/// <inheritdoc/>
		public override string ToString() {
			return string.Format("{0} - {1}", recursionCounter, options);
		}
	}
}
